﻿using MengCC.Common.IO;
using MengCC.Core.IO;
using MengCC.Core.Plugins;
using MengCC.Core.Projects;
using MengCC.Plugin.SDK;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MengCC.Core.Works
{
    public class WorkManager
    {
        private static Dictionary<int, bool> workLock;
        private PluginManager pluginManager;
        private ProjectManager projectManager;
        private IOManager iOManager;
        private readonly string workspaceRootDir;

        static WorkManager()
        {
            workLock = new Dictionary<int, bool>();
        }
        public WorkManager(PluginManager pluginManager, ProjectManager projectManager, IOManager iOManager, string workspaceRootDir)
        {
            this.pluginManager = pluginManager;
            this.projectManager = projectManager;
            this.iOManager = iOManager;
            this.workspaceRootDir = workspaceRootDir;
            if (!Directory.Exists(workspaceRootDir)) Directory.CreateDirectory(workspaceRootDir);
        }


        private bool GetLock(int projectId)
        {
            if (workLock.ContainsKey(projectId))
                return workLock[projectId];
            return false;
        }

        private void SetLock(int projectId, bool state)
        {
            if (!workLock.ContainsKey(projectId))
            {
                lock ($"DoWork-{projectId}")
                {
                    if (!workLock.ContainsKey(projectId))
                    {
                        workLock.Add(projectId, state);
                        return;
                    }
                }
            }
            workLock[projectId] = state;
        }

        /// <summary>
        /// 开启任务
        /// </summary>
        /// <param name="projectId"></param>
        public void StartWork(int projectId)
        {
            lock ($"StartWork-{projectId}")
            {
                if (GetLock(projectId)) return;
            }
            //TODO 运行任务
            BuildAsync(projectId);
        }


        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="project"></param>
        public void RemoveWorkspace(Project project)
        {
            try
            {
                var workspace = Path.Combine(workspaceRootDir, project.Id.ToString(), project.Name);
                if (Directory.Exists(workspace))
                    DirectoryHelper.RemoveAll(workspace);
            }
            catch (Exception ex)
            {
                //log
            }
        }

        /// <summary>
        /// 获取插件上下文
        /// </summary>
        /// <param name="pluginConfig"></param>
        /// <param name="projectId"></param>
        /// <returns></returns>
        private PluginContext GetPluginContext(PluginInfo pluginInfo, Project project, BuildLog buildLog)
        {
            //组合工作空间
            var workspace = Path.Combine(workspaceRootDir, project.Id.ToString(), project.Name, pluginInfo.Path ?? "");
            var context = new PluginContext()
            {

                Workspace = workspace,
                Config = pluginInfo.Config
            };

            if (!Directory.Exists(workspace)) Directory.CreateDirectory(workspace);

            context.Config = context.Config.Replace("{WORKSPACE}", workspace.Replace("\\", "\\\\"));
            //如果是构建结束后
            if (buildLog.BuildState != BuildLog.BuildStateEnum.Building)
            {
                context.Config = context.Config.Replace("{BUILDRESULT}", buildLog.OutContent);
                context.Config = context.Config.Replace("{BUILDSTATE}", buildLog.BuildState.ToString());
                context.Config = context.Config.Replace("{BUILDTIME}", buildLog.BuildTime.ToString());
            }

            return context;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="pluginInfo"></param>
        /// <param name="projectId"></param>
        /// <returns></returns>
        private bool RunPlugin(PluginInfo pluginInfo, Project project, BuildLog buildLog)
        {
            try
            {
                //如果检测到停止，则退出构建
                if (!GetLock(project.Id))
                {
                    iOManager.EnqueueOutput(project.Id.ToString(), $"构建失败，终止构建");
                    return false;
                }

                var plugin = pluginManager.GetPluginById(pluginInfo.Id);
                if (plugin == null)
                {
                    iOManager.EnqueueOutput(project.Id.ToString(), $"构建失败：无效的插件 {pluginInfo.Id}");
                    return false;
                }
                var pluginContext = GetPluginContext(pluginInfo, project, buildLog);
                var process = GetProcess(pluginContext, plugin);
                var result = true;
                process.Start();
                var task = new Task[] {
                    ProcessInputAsync(process, buildLog.GetIOKey()),
                    ProcessOutputAsync(process, buildLog.GetIOKey()),
                    ProcessErrorAsync(process, buildLog.GetIOKey())
                };
                Task.WaitAll(task);
                if (iOManager.DequeueOutput(buildLog.GetIOKey()).Contains("Exception")) result = false;
                return result;
            }
            catch (Exception ex)
            {
                iOManager.EnqueueOutput(buildLog.GetIOKey(), ex.ToString());
                return false;
            }
        }
        /// <summary>
        /// 异步处理输入
        /// </summary>
        /// <param name="process"></param>
        /// <param name="projectid"></param>
        /// <param name="iokey"></param>
        /// <returns></returns>
        private async Task ProcessInputAsync(Process process, string iokey)
        {
            await Task.Delay(0);
            //加入输入事件监听，如果有输入，则插入数据
            iOManager.AddEventHandler(iOManager.GetDefaultIOEnventHandler(Guid.NewGuid().ToString(),iokey, input =>
            {
                if (!process.HasExited)
                {
                    iOManager.EnqueueOutput(iokey, input);
                    using (var inputStream = process.StandardInput)
                    {
                        inputStream.WriteLine(input);
                        inputStream.Flush();
                    }
                }
            }, IIOEnventHandler.EventTypeEnum.Input));


            //while (!process.HasExited)
            //{
            //    var input = iOManager.DequeueInput(iokey);
            //    if (process.HasExited) break;
            //    iOManager.EnqueueOutput(iokey, input);
            //    using (var inputStream = process.StandardInput)
            //    {
            //        inputStream.WriteLine(input);
            //        inputStream.Flush();
            //    }
            //}

        }
        /// <summary>
        /// 异步输出
        /// </summary>
        /// <param name="process"></param>
        /// <param name="iokey"></param>
        /// <returns></returns>
        private async Task ProcessOutputAsync(Process process, string iokey)
        {
            await Task.Delay(100);

            //开始读取

            using (var sr = process.StandardOutput)
            {
                while (sr.BaseStream.CanRead && !sr.EndOfStream)
                {
                    var content = sr.ReadLine();
                    iOManager.EnqueueOutput(iokey, content);
                }
            }
            using (var sr = process.StandardError)
            {
                while (sr.BaseStream.CanRead && !sr.EndOfStream)
                {
                    var content = sr.ReadLine();
                    iOManager.EnqueueOutput(iokey, content);
                }
                if (!process.HasExited)
                {
                    process.Kill();
                }
                iOManager.EnqueueInput(iokey, "exit");
            }
        }
        /// <summary>
        /// 异步异常输出
        /// </summary>
        /// <param name="process"></param>
        /// <param name="iokey"></param>
        /// <returns></returns>
        private async Task ProcessErrorAsync(Process process, string iokey)
        {
            await Task.Delay(100);

            //开始读取
            using (var sr = process.StandardError)
            {
                while (!sr.EndOfStream)
                {
                    var content = sr.ReadLine();
                    iOManager.EnqueueOutput(iokey, content);
                }
            }
        }

        /// <summary>
        /// 创建进程
        /// </summary>
        /// <param name="cmd"></param>
        /// <returns></returns>
        private Process GetProcess(PluginContext pluginContext, Plugins.Plugin plugin)
        {
            ProcessStartInfo processInfo = null;
            if (plugin.Platform.ToLower().StartsWith("core"))
                processInfo = new ProcessStartInfo("dotnet", pluginManager.GetDotnetCommand(plugin, pluginContext.ToString()));
            else if (plugin.Platform.ToLower().StartsWith("win"))
                processInfo = new ProcessStartInfo(pluginManager.GetFrameworkCommand(plugin), pluginContext.ToString());

            processInfo.RedirectStandardInput = true;
            processInfo.RedirectStandardOutput = true;
            processInfo.CreateNoWindow = true;
            processInfo.RedirectStandardError = true;
            processInfo.Verb = "runas";

            //processInfo.StandardOutputEncoding = Encoding.UTF8;
            var process = new Process();
            process.StartInfo = processInfo;

            return process;
        }

        /// <summary>
        /// 构建任务
        /// </summary>
        /// <param name="projectId"></param>
        /// <returns></returns>
        private async Task BuildAsync(int projectId)
        {
            await Task.Delay(100);
            var project = projectManager.GetProject(projectId);
            if (project == null) return;
            var buildLog = projectManager.AddBuildLog(project);
            var ioKey = buildLog.GetIOKey();
            try
            {
                //加锁
                SetLock(projectId, true);

                //执行
                iOManager.EnqueueOutput(ioKey, "-----开始构建任务-----");

                iOManager.EnqueueOutput(ioKey, "-----开始同步-----");
                var buildState = true;

                //如果勾选在构建前删除，则清理工作空间
                if (project.IsClearBeforeBuilding)
                    RemoveWorkspace(project);

                //调用同步插件
                if (project.RepositoryPlugins != null && project.RepositoryPlugins.Count > 0)
                {
                    foreach (var pluginInfo in project.RepositoryPlugins)
                    {
                        //SetEnvironmentVariables(pluginInfo, project);
                        buildState = RunPlugin(pluginInfo, project, buildLog);
                        if (!buildState) break;
                    }
                }
                //如果前面的执行失败，则不再继续
                if (buildState)
                {
                    iOManager.EnqueueOutput(ioKey, "-----开始构建-----");
                    //调用构建插件
                    if (project.BuildPlugins != null && project.BuildPlugins.Count > 0)
                    {
                        foreach (var pluginInfo in project.BuildPlugins)
                        {
                            buildState = RunPlugin(pluginInfo, project, buildLog);
                            if (!buildState) break;
                        }
                    }
                }
                iOManager.EnqueueOutput(ioKey, "-----完成-----");

                buildLog.BuildState = buildState ? BuildLog.BuildStateEnum.Successed : BuildLog.BuildStateEnum.Failed;
                buildLog.OutContent = iOManager.DequeueOutput(ioKey);
                projectManager.UpdateBuildLog(buildLog);


                iOManager.EnqueueOutput(ioKey, "-----构建后-----");
                //调用构建后插件
                if (project.BuildedPlugins != null && project.BuildedPlugins.Count > 0)
                {
                    foreach (var pluginInfo in project.BuildedPlugins)
                    {
                        buildState = RunPlugin(pluginInfo, project, buildLog);
                        if (!buildState) break;
                    }
                }


                buildLog.OutContent = iOManager.DequeueOutput(ioKey);
                buildLog.BuildedTime = DateTime.Now;
                projectManager.UpdateBuildLog(buildLog);
                iOManager.RemoveQueue(ioKey);
            }
            catch (Exception ex)
            {
                buildLog.BuildState = BuildLog.BuildStateEnum.Failed;
                iOManager.EnqueueOutput(ioKey, "-----开始异常-----");
                iOManager.EnqueueOutput(ioKey, $"异常：{ex}");
                buildLog.OutContent = iOManager.DequeueOutput(ioKey);
            }
            finally
            {
                projectManager.UpdateBuildLog(buildLog);

                //解锁
                SetLock(projectId, false);
            }



        }
        /// <summary>
        /// 停止任务
        /// </summary>
        /// <param name="projectId"></param>
        public void StopWork(int projectId)
        {
            if (workLock.ContainsKey(projectId))
                workLock[projectId] = false;
        }
        /// <summary>
        /// 停止所有任务
        /// </summary>
        /// <param name="projectId"></param>
        public void StopAllWork()
        {
            var lenth = workLock.Count;
            for (int i = 0; i < lenth; i++)
            {
                workLock[i] = false;
            }
        }
        /// <summary>
        /// 获取在构建的项目
        /// </summary>
        public List<int> GetBuildingProjects()
        {
            return workLock.Keys.ToList();
        }
    }
}
